package cn.shop.admin.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.RegexPool;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.shop.admin.component.withdraw.WithdrawContext;
import cn.shop.admin.component.withdraw.WithdrawStrategy;
import cn.shop.admin.entity.PayCallBack;
import cn.shop.admin.req.BatchWithdrawReq;
import cn.shop.admin.req.ExportWithdrawReq;
import cn.shop.admin.service.BackSettingService;
import cn.shop.admin.service.FrontUserService;
import cn.shop.admin.service.WithdrawService;
import cn.shop.base.MyPage;
import cn.shop.transcation.backSetting.entity.BackSetting;
import cn.shop.transcation.frontUserModule.FrontUser;
import cn.shop.transcation.withdraw.entity.Withdraw;
import cn.shop.utils.ApiResult;
import cn.shop.utils.EnumUtil;
import cn.shop.utils.Putif;
import cn.shop.utils.constant.Constant;
import cn.shop.utils.constant.SystemSettingConstant;
import cn.shop.utils.enums.ProcessState;
import cn.shop.utils.enums.WithdrawAccountType;
import cn.shop.utils.enums.WithdrawSolution;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 提现
 *
 * @author jzw
 * @since 2021-11-10 10:06:01
 */
@Slf4j
@Validated
@RestController
@RequestMapping("/api/v1/withdraw")
public class WithdrawController {

	/**
	 * 服务对象
	 */
	@Resource
	private WithdrawService withdrawService;

	/**
	 * 服务对象
	 */
	@Resource
	private BackSettingService backSettingService;
	@Resource
	private FrontUserService frontUserService;

	/**
	 * 待出款总额
	 *
	 * @author jzw
	 * @since 2021/11/12 10:53
	 */
	@GetMapping("totalAmountForProcessing")
	public ApiResult<Map<String, Object>> totalAmountForProcessing(Withdraw withdraw,
	                                                               @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime startTime,
	                                                               @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime endTime) {
		if (withdraw == null) {
			withdraw = new Withdraw();
		}
		withdraw.setState(ProcessState.PROCESSING);
		return statistics(withdraw, startTime, endTime);
	}

	/**
	 * 待处理总额
	 *
	 * @author jzw
	 * @since 2021/11/12 10:53
	 */
	@GetMapping("totalAmountForUntreated")
	public ApiResult<Map<String, Object>> totalAmountForUntreated(Withdraw withdraw,
	                                                              @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime startTime,
	                                                              @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime endTime) {
		if (withdraw == null) {
			withdraw = new Withdraw();
		}
		withdraw.setState(ProcessState.UNTREATED);
		return statistics(withdraw, startTime, endTime);
	}

	/**
	 * 异常总额
	 *
	 * @author jzw
	 * @since 2021/11/12 10:53
	 */
	@GetMapping("totalAmountForAbnormal")
	public ApiResult<Map<String, Object>> totalAmountForAbnormal(Withdraw withdraw,
	                                                             @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime startTime,
	                                                             @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime endTime) {

		if (withdraw == null) {
			withdraw = new Withdraw();
		}
		withdraw.setState(ProcessState.ABNORMAL);
		return statistics(withdraw, startTime, endTime);
	}

	/**
	 * 完成总额
	 *
	 * @author lyh
	 * @since 2022/04/04
	 */
	@GetMapping("totalAmountForCompleted")
	public ApiResult<Map<String, Object>> totalAmountForCompleted(Withdraw withdraw,
	                                                              @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime startTime,
	                                                              @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime endTime) {

		if (withdraw == null) {
			withdraw = new Withdraw();
		}
		withdraw.setState(ProcessState.COMPLETED);
		return statistics(withdraw, startTime, endTime);
	}

	/**
	 * 分页统计信息
	 *
	 * @param withdraw  查询条件
	 * @param startTime 开始时间
	 * @param endTime   结束时间
	 * @return 返回结果
	 */
	@GetMapping("/statistics")
	public ApiResult<Map<String, Object>> statistics(Withdraw withdraw, LocalDateTime startTime, LocalDateTime endTime) {
		QueryWrapper<Withdraw> query = Wrappers.query(withdraw);
		query.eq(withdraw.getState()!=null,"w.state",withdraw.getState())
				.eq(withdraw.getSolution()!=null,"w.solution",withdraw.getSolution())
				.eq(withdraw.getAccountType()!=null,"w.account_type",withdraw.getAccountType())
				.eq(withdraw.getId()!=null,"w.id",withdraw.getId())
				.like(StrUtil.isNotBlank(withdraw.getAccount()),"w.account",withdraw.getAccount())
				.like(StrUtil.isNotBlank(withdraw.getBankAccountName()),"w.bank_account_name",withdraw.getBankAccountName());
		query.select("count(*) count", "IFNULL(sum(w.amount),0) total", "IFNULL(sum(w.actual_amount),0) actual");
		if (withdraw.getState() == ProcessState.COMPLETED) {
			query.apply(startTime != null, "w.payment_time >= {0}", startTime)
					.apply(endTime != null, "w.payment_time <= {0}", endTime);
		} else {
			query.apply(startTime != null, "w.update_time >= {0}", startTime)
					.apply(endTime != null, "w.update_time <= {0}", endTime);
		}
		return ApiResult.success(withdrawService.getStatistics(query));
	}

	/**
	 * 批量提现
	 *
	 * @param batchWithdrawReq 提现订单和提现类型
	 * @author jzw
	 * @since 2021/11/12 10:53
	 */
	@PostMapping("batchWithdraw")
	public ApiResult<Map<String, Object>> batchWithdraw(@Validated @RequestBody BatchWithdrawReq batchWithdrawReq, HttpServletRequest request) {
		if (batchWithdrawReq.getType() == WithdrawSolution.ALIPAY_PAYMENT && batchWithdrawReq.getId() == null) {
			return ApiResult.fail("请选择付款渠道");
		}
		BackSetting backSetting = backSettingService.getOneByKey(SystemSettingConstant.ADMIN_TRANSACTION_PASSWORD);
		if (StrUtil.isBlank(backSetting.getValue())) {
			return ApiResult.fail("请设置交易密码");
		}
		if (!DigestUtil.bcryptCheck(batchWithdrawReq.getPwd(), backSetting.getValue())) {
			return ApiResult.fail("交易密码不正确");
		}
		String scheme = request.getHeader("origin");
		batchWithdrawReq.setScheme(scheme);
		WithdrawStrategy instance = WithdrawContext.getInstance(batchWithdrawReq.getType().getCode());
		if (null != instance) {
			/*if (batchWithdrawReq.getType() == WithdrawSolution.ALIPAY_PAYMENT) {

			}
			else {
				return ApiResult.result(instance.settlement(batchWithdrawReq));
			}*/
			instance.settlement(batchWithdrawReq);
			return ApiResult.success("提现结果请稍后查看");
		}
		return ApiResult.fail("提现类型不存在");
	}

	/**
	 * 导出提现数据
	 *
	 * @param req 编号
	 * @author jzw
	 * @since 2022/1/11 16:57
	 */
	@PostMapping("exportWithdrawByIds")
	public void exportWithdraw(@RequestBody ExportWithdrawReq req, HttpServletResponse response) {
		withdrawService.exportWithdraw(req.getPwd(), req.getIds(), response, req.getType(), "faka");
	}

	/**
	 * 导出云支付提现数据
	 *
	 * @param req 编号
	 * @author jzw
	 * @since 2022/1/11 16:57
	 */
	@PostMapping("exportWithdrawByIds2")
	public void exportWithdraw2(@RequestBody ExportWithdrawReq req, HttpServletResponse response) {
		withdrawService.exportWithdraw(req.getPwd(), req.getIds(), response, req.getType(), "yun");
	}

	/**
	 * 分页查询所有数据
	 *
	 * @param page     分页对象
	 * @param withdraw 查询实体
	 * @return 所有数据
	 * @author jzw
	 * @since 2021-11-10 10:06:01
	 */
	@GetMapping("queryByPage")
	public ApiResult<MyPage<Withdraw>> queryByPage(MyPage<Withdraw> page, Withdraw withdraw,
	                                               @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime startTime,
	                                               @RequestParam(required = false) @DateTimeFormat(pattern = Constant.DATETIME_FORMAT) LocalDateTime endTime) {

		QueryWrapper<Withdraw> query = Wrappers.query(withdraw);
		query.eq(withdraw.getState()!=null,"w.state",withdraw.getState())
				.eq(withdraw.getSolution()!=null,"w.solution",withdraw.getSolution())
				.eq(withdraw.getAccountType()!=null,"w.account_type",withdraw.getAccountType())
				.eq(withdraw.getId()!=null,"w.id",withdraw.getId())
				.like(StrUtil.isNotBlank(withdraw.getAccount()),"w.account",withdraw.getAccount())
				.like(StrUtil.isNotBlank(withdraw.getBankAccountName()),"w.bank_account_name",withdraw.getBankAccountName());
		if (withdraw.getState() == ProcessState.COMPLETED) {
			page.addOrder(OrderItem.desc("w.payment_time"));
			query.apply(startTime != null, "w.payment_time >= {0}", startTime)
					.apply(endTime != null, "w.payment_time <= {0}", endTime);
		} else {
			page.addOrder(OrderItem.desc("update_time"));
			query.apply(startTime != null, "w.update_time >= {0}", startTime)
					.apply(endTime != null, "w.update_time <= {0}", endTime);
		}
		MyPage<Withdraw> result = withdrawService.page(page, query);
		Set<Long> uids = result.getRecords().stream()
				.filter(w -> w.getType() == 2)
				.map(Withdraw::getUserId)
				.collect(Collectors.toSet());
		Map<Long, FrontUser> users = Collections.emptyMap();
		if (CollectionUtil.isNotEmpty(uids)) {
			users = frontUserService.lambdaQuery()
					.in(FrontUser::getId, uids)
					.list().stream()
					.collect(Collectors.toMap(FrontUser::getId, f -> f));
		}
		final Map<Long, FrontUser> map = users;
		result.getRecords().forEach(e -> {
			if (e.getAccountType() == WithdrawAccountType.BANK_PAYMENT) {
				e.setAccount(DesensitizedUtil.bankCard(e.getAccount()));
			} else {
				String str;
				if (PhoneUtil.isMobile(e.getAccount())) {
					str = DesensitizedUtil.mobilePhone(e.getAccount());
				}
				else if (ReUtil.isMatch(RegexPool.EMAIL, e.getAccount())) {
					str = DesensitizedUtil.email(e.getAccount());
				}
				else {
					str = StrUtil.hide(e.getAccount(), 2, e.getAccount().length());
				}
				e.setAccount(str);
			}
			e.setPhone(DesensitizedUtil.mobilePhone(e.getPhone()));
			e.setBankAccountName(DesensitizedUtil.chineseName(e.getBankAccountName()));
			FrontUser user = map.get(e.getUserId());
			e.setWarn(user != null && user.getBalance().doubleValue() < 0);
			// 添加玩家账号
			if (user != null){
				e.setPlayerAccount(user.getAccount());
			}
		});
		return ApiResult.success(result);
	}

	/**
	 * 获取某个状态下的提现统计数量
	 *
	 * @param state 状态
	 * @return 返回结果
	 */
	@GetMapping("/statistics/{state}")
	public ApiResult<Map<String, Object>> typeCount(@PathVariable int state) throws InvocationTargetException, IllegalAccessException {
		ProcessState s = EnumUtil.getEnum(ProcessState.class, state);
		if (s == null) {
			return ApiResult.fail("状态不存在");
		}
		long bankCount = withdrawService.count(Wrappers.<Withdraw>lambdaQuery()
				.eq(Withdraw::getState, s)
				.eq(Withdraw::getSolution, WithdrawSolution.PAYMENT_ON_BEHALF));
		long aliCount = withdrawService.count(Wrappers.<Withdraw>lambdaQuery()
				.eq(Withdraw::getState, s)
				.eq(Withdraw::getSolution, WithdrawSolution.ALIPAY_PAYMENT));
		return ApiResult.success(Putif.start()
				.anyway("bank", bankCount)
				.anyway("alipay", aliCount).get());
	}

	/**
	 * 通过主键查询单条数据
	 *
	 * @param id 主键
	 * @return 单条数据
	 * @author jzw
	 * @since 2021-11-10 10:06:01
	 */
	@GetMapping("queryOne/{id}")
	public ApiResult<Withdraw> selectOne(@PathVariable Serializable id) {
		return ApiResult.success(withdrawService.getById(id));
	}

	/**
	 * 新增数据
	 *
	 * @param withdraw 实体对象
	 * @return 新增结果
	 * @author jzw
	 * @since 2021-11-10 10:06:01
	 */
	@PostMapping("insert")
	public ApiResult<Map<String, Object>> insert(@RequestBody Withdraw withdraw) {
		return ApiResult.result(withdrawService.save(withdraw));
	}

	/**
	 * 修改数据
	 *
	 * @param withdraw 实体对象
	 * @return 修改结果
	 * @author jzw
	 * @since 2021-11-10 10:06:01
	 */
	@PutMapping("update")
	public ApiResult<Map<String, Object>> update(@RequestBody Withdraw withdraw) {
		return ApiResult.result(withdrawService.updateById(withdraw));
	}

	/**
	 * 手动完成提现代付
	 *
	 * @param ids 实体对象
	 * @return 修改结果
	 * @author jzw
	 * @since 2021-11-10 10:06:01
	 */
	@PutMapping("completePayment")
	public ApiResult<Map<String, Object>> completePayment(@RequestBody List<Long> ids) {
		return ApiResult.result(withdrawService.completePayment(ids));
	}

	/**
	 * 删除单条数据
	 *
	 * @param id 主键
	 * @return 删除结果
	 * @author jzw
	 * @since 2021-11-10 10:06:01
	 */
	@DeleteMapping("delete/{id}")
	public ApiResult<Map<String, Object>> delete(@PathVariable Long id) {
		Withdraw withdraw = new Withdraw();
		withdraw.setId(id);
		return ApiResult.result(withdrawService.removeById(withdraw));
	}

	/**
	 * 批量删除数据
	 *
	 * @param idList 主键结合
	 * @return 删除结果
	 * @author jzw
	 * @since 2021-11-10 10:06:01
	 */
	@DeleteMapping("batchDelete")
	public ApiResult<Map<String, Object>> batchDelete(@RequestParam("idList") List<Long> idList) {
		return ApiResult.result(withdrawService.batchDelete(idList));

	}

	/**
	 * 异常出款退回接口
	 *
	 * @param ids 提现编号
	 * @return 返回结果
	 */
	@PutMapping("/sendBack")
	public ApiResult<Map<String, Object>> sendBackWithdraw(@RequestBody List<Long> ids) {
		return ApiResult.result(withdrawService.sendBackWithdraw(ids));
	}

	@PostMapping("/notify")
	public String notify(@RequestBody PayCallBack req) {
		String result = withdrawService.notify(req);
		log.info("代付回调接收参数：{}，回调结果: {}", req, result);
		return result;
	}
}

